﻿//------------------------------------------------------------------------------
//  Namespace: FruitVentDesign.Controls
//  
//  Function： N/A
//  Name： MultiSelect
//  
//  Ver       Time                     Author
//  0.10      2021/6/23 14:43:25      FruitVent
//
//  此代码版权归作者本人FruitVent所有
//  源代码使用协议遵循本仓库的开源协议及附加协议，若本仓库没有设置，则按MIT开源协议授权
//  CSDN博客：https://blog.csdn.net/weixin_39552347
//  源代码仓库：https://gitee.com/fruitvent
//  感谢您的下载和使用
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
using FruitVentDesign.Commands;
using FruitVentDesign.Models;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;

namespace FruitVentDesign.Controls
{
    public class MultiSelect : ComboBox
    {
        static MultiSelect()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(MultiSelect), new FrameworkPropertyMetadata(typeof(MultiSelect)));
            //var defaultMetadata = ComboBox.TextProperty.GetMetadata(typeof(ComboBox));

            //ComboBox.TextProperty.OverrideMetadata(typeof(MultiSelect), new FrameworkPropertyMetadata(
            //    string.Empty, FrameworkPropertyMetadataOptions.Journal | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
            //    defaultMetadata.PropertyChangedCallback,
            //    defaultMetadata.CoerceValueCallback,
            //    true,
            //    UpdateSourceTrigger.PropertyChanged));
        }

        public MultiSelect()
        {
            Loaded += MultiSelect_Loaded;
        }

        private void MultiSelect_Loaded(object sender, RoutedEventArgs e)
        {
            ChekedItems = new ObservableCollection<SelectlistItem>();
            SelectList = new ObservableCollection<SelectlistItem>();
            if (ItemsSource != null)
            {
                foreach (var item in ItemsSource)
                {
                    SelectlistItem selectlistItemV = new SelectlistItem
                    {
                        IsSelected = false,
                        Id = item.GetType().GetProperty(SelectItemKey).GetValue(item, null).ToString(),
                        Name = item.GetType().GetProperty(SelectItemValue).GetValue(item, null).ToString()
                    };
                    var key = item.GetType().GetProperty(SelectItemKey).GetValue(item, null).ToString();
                    if (SelectedItems != null)
                    {
                        foreach (var subItem in SelectedItems)
                        {
                            var subKey = subItem.GetType().GetProperty(SelectItemKey).GetValue(subItem, null).ToString();
                            if (key == subKey)
                            {
                                selectlistItemV.IsSelected = true;
                                break;
                            }
                        }
                    }
                    SelectList.Add(selectlistItemV);
                }
            }

            if (SelectedItems != null)
            {
                string temp = "";
                foreach (var item in SelectedItems)
                {
                    SelectlistItem selectlistItemH = new SelectlistItem
                    {
                        IsSelected = true,
                    };
                    var key = item.GetType().GetProperty(SelectItemKey).GetValue(item, null).ToString();
                    foreach (var subItem in ItemsSource)
                    {
                        var subKey = subItem.GetType().GetProperty(SelectItemKey).GetValue(subItem, null).ToString();
                        if (key == subKey)
                        {
                            selectlistItemH.Id = subItem.GetType().GetProperty(SelectItemKey).GetValue(subItem, null).ToString();
                            selectlistItemH.Name = subItem.GetType().GetProperty(SelectItemValue).GetValue(subItem, null).ToString();
                            break;
                        }
                    }
                    ChekedItems.Add(selectlistItemH);
                    temp += key;
                }
                Text = temp;
            }

            _ListBoxH.ItemsSource = ChekedItems;
            _ListBoxV.ItemsSource = SelectList;
            _ListBoxV.SelectionChanged += ListBoxV_SelectionChanged;
            _ListBoxH.SelectionChanged += ListBoxH_SelectionChanged;
            _ListBoxH.AddHandler(ListBox.MouseWheelEvent, new MouseWheelEventHandler(List_MouseWheel), true);//添加ListBox横向滚动监听事件
        }

        #region HasErrorMessage 验证通过与否字段
        /// <summary>
        /// 验证通过与否字段
        /// </summary>
        public static readonly DependencyProperty HasErrorMessageProperty = DependencyProperty.Register(
            "HasErrorMessage", typeof(bool), typeof(MultiSelect), new FrameworkPropertyMetadata(false,
                FrameworkPropertyMetadataOptions.Journal | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
                new PropertyChangedCallback(OnHasErrorMessagePropertyChanged)));

        private static void OnHasErrorMessagePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            var oldValue = (object)args.OldValue;
            var newValue = (object)args.NewValue;

            if (oldValue == newValue)
                return;

            if ((bool)newValue)
            {
                if (obj is FrameworkElement content && content.Parent is Form form)
                {
                    if (form.ItemContainerGenerator.ContainerFromItem(content) is FormItem formItem)
                    {
                        formItem.HasErrorMessage = true;
                        var binding = new Binding(nameof(FormItem.ErrorMessage))
                        {
                            Source = content,
                            Mode = BindingMode.TwoWay,
                            Path = new PropertyPath("(Validation.Errors).CurrentItem.ErrorContent")
                        };
                        formItem.SetBinding(FormItem.ErrorMessageProperty, binding);
                    }
                }
            }
            else
            {
                if (obj is FrameworkElement content && content.Parent is Form form)
                {
                    if (form.ItemContainerGenerator.ContainerFromItem(content) is FormItem formItem)
                    {
                        formItem.HasErrorMessage = false;
                    }
                }
            }
        }

        public bool HasErrorMessage
        {
            get { return (bool)GetValue(HasErrorMessageProperty); }
            set { SetValue(HasErrorMessageProperty, value); }
        }

        public static void SetHasErrorMessage(DependencyObject element, bool value)
        {
            element.SetValue(HasErrorMessageProperty, value);
        }

        public static bool GetHasErrorMessage(DependencyObject element)
        {
            return (bool)element.GetValue(HasErrorMessageProperty);
        }
        #endregion

        #region PlaceholderProperty 占位符
        /// <summary>
        /// 占位符
        /// </summary>
        public static readonly DependencyProperty PlaceholderProperty = DependencyProperty.Register(
            "Placeholder", typeof(string), typeof(MultiSelect), new FrameworkPropertyMetadata(""));

        public string Placeholder
        {
            get { return (string)GetValue(PlaceholderProperty); }
            set { SetValue(PlaceholderProperty, value); }
        }

        public static string GetPlaceholder(DependencyObject d)
        {
            return (string)d.GetValue(PlaceholderProperty);
        }

        public static void SetPlaceholder(DependencyObject obj, string value)
        {
            obj.SetValue(PlaceholderProperty, value);
        }
        #endregion

        #region CornerRadiusProperty Border圆角
        /// <summary>
        /// Border圆角
        /// </summary>
        public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.RegisterAttached(
            "CornerRadius", typeof(CornerRadius), typeof(MultiSelect), new FrameworkPropertyMetadata(new CornerRadius(3)));

        public CornerRadius CornerRadius
        {
            get { return (CornerRadius)GetValue(CornerRadiusProperty); }
            set { SetValue(CornerRadiusProperty, value); }
        }

        public static CornerRadius GetCornerRadius(DependencyObject d)
        {
            return (CornerRadius)d.GetValue(CornerRadiusProperty);
        }

        public static void SetCornerRadius(DependencyObject obj, CornerRadius value)
        {
            obj.SetValue(CornerRadiusProperty, value);
        }
        #endregion

        #region FocusBorderBrushProperty 焦点边框色，输入控件
        /// <summary>
        /// 焦点边框色，输入控件
        /// </summary>
        public static readonly DependencyProperty FocusBorderBrushProperty = DependencyProperty.Register(
            "FocusBorderBrush", typeof(Brush), typeof(MultiSelect), new FrameworkPropertyMetadata(null));

        public Brush FocusBorderBrush
        {
            get { return (Brush)GetValue(FocusBorderBrushProperty); }
            set { SetValue(FocusBorderBrushProperty, value); }
        }

        public static void SetFocusBorderBrush(DependencyObject element, Brush value)
        {
            element.SetValue(FocusBorderBrushProperty, value);
        }

        public static Brush GetFocusBorderBrush(DependencyObject element)
        {
            return (Brush)element.GetValue(FocusBorderBrushProperty);
        }
        #endregion

        #region MouseOverBorderBrushProperty 鼠标进入边框色，输入控件
        /// <summary>
        /// 鼠标进入边框色，输入控件
        /// </summary>
        public static readonly DependencyProperty MouseOverBorderBrushProperty = DependencyProperty.Register(
            "MouseOverBorderBrush", typeof(Brush), typeof(MultiSelect),
            new FrameworkPropertyMetadata(Brushes.Transparent,
            FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.Inherits));

        public Brush MouseOverBorderBrush
        {
            get { return (Brush)GetValue(MouseOverBorderBrushProperty); }
            set { SetValue(MouseOverBorderBrushProperty, value); }
        }

        /// <summary>
        /// Sets the brush used to draw the mouse over brush.
        /// </summary>
        public static void SetMouseOverBorderBrush(DependencyObject obj, Brush value)
        {
            obj.SetValue(MouseOverBorderBrushProperty, value);
        }

        /// <summary>
        /// Gets the brush used to draw the mouse over brush.
        /// </summary>
        public static Brush GetMouseOverBorderBrush(DependencyObject obj)
        {
            return (Brush)obj.GetValue(MouseOverBorderBrushProperty);
        }
        #endregion

        #region AllowClearProperty 清除输入框Text值按钮启用
        /// <summary>
        /// 清除输入框Text值按钮显示与否
        /// </summary>
        public static readonly DependencyProperty AllowClearProperty = DependencyProperty.RegisterAttached(
            "AllowClear", typeof(bool), typeof(MultiSelect), new FrameworkPropertyMetadata(false, AllowClearChanged));

        public bool AllowClear
        {
            get { return (bool)GetValue(AllowClearProperty); }
            set { SetValue(AllowClearProperty, value); }
        }

        /// <summary>
        /// Gets the brush used to draw the mouse over brush.
        /// </summary>
        public static bool GetAllowClear(DependencyObject d)
        {
            return (bool)d.GetValue(AllowClearProperty);
        }

        public static void SetAllowClear(DependencyObject obj, bool value)
        {
            obj.SetValue(AllowClearProperty, value);
        }

        private static void AllowClearChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (e.OldValue != e.NewValue && d is Button button)
            {
                button.CommandBindings.Add(RoutedUICommands.ClearTextCommandBinding);
            }
        }
        #endregion

        #region SelectedItems
        public static readonly DependencyProperty SelectedItemsProperty = DependencyProperty.RegisterAttached(
            "SelectedItems", typeof(IEnumerable), typeof(MultiSelect), new FrameworkPropertyMetadata(null,
                FrameworkPropertyMetadataOptions.Journal | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
                new PropertyChangedCallback(OnSelectedItemsPropertyChanged),
                new CoerceValueCallback(CoerceValue)));

        static object CoerceValue(DependencyObject obj, object value)
        {
            return value;
        }

        private static void OnSelectedItemsPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            var oldValue = (IEnumerable)args.OldValue;
            var newValue = (IEnumerable)args.NewValue;

            if (oldValue == newValue)
                return;

        }

        public IEnumerable SelectedItems
        {
            get { return (IEnumerable)GetValue(SelectedItemsProperty); }
            set { SetValue(SelectedItemsProperty, value); }
        }
        #endregion

        #region dependency proeprties
        /// <summary>
        /// 列表显示唯一标识依赖属性
        /// </summary>
        public static readonly DependencyProperty SelectItemKeyProperty = DependencyProperty.Register(
           "SelectItemKey",
           typeof(string),
           typeof(MultiSelect),
           new FrameworkPropertyMetadata(null)
           );

        public string SelectItemKey
        {
            get { return (string)GetValue(SelectItemKeyProperty); }
            set { SetValue(SelectItemKeyProperty, value); }
        }

        /// <summary>
        /// 列表显示依赖属性
        /// </summary>
        public static readonly DependencyProperty SelectItemValueProperty = DependencyProperty.Register(
          "SelectItemValue",
          typeof(string),
          typeof(MultiSelect),
          new FrameworkPropertyMetadata(null)
          );

        public string SelectItemValue
        {
            get { return (string)GetValue(SelectItemValueProperty); }
            set { SetValue(SelectItemValueProperty, value); }
        }

        public static string GetSelectItemValue(DependencyObject element)
        {
            return (string)element.GetValue(SelectItemValueProperty);
        }

        public static void SetSelectItemValue(DependencyObject element, string value)
        {
            element.SetValue(SelectItemValueProperty, value);
        }
        #endregion

        /// <summary>
        /// 选中项列表
        /// </summary>
        public ObservableCollection<SelectlistItem> ChekedItems = new ObservableCollection<SelectlistItem>();
        public ObservableCollection<SelectlistItem> SelectList = new ObservableCollection<SelectlistItem>();

        /// <summary>
        /// ListBox竖向列表
        /// </summary>
        private ListBox _ListBoxV;

        /// <summary>
        /// ListBox横向列表
        /// </summary>
        private ListBox _ListBoxH;

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            _ListBoxV = Template.FindName("PART_ListBox", this) as ListBox;//列表
            _ListBoxH = Template.FindName("PART_ListBoxChk", this) as ListBox;//回显
        }

        /// <summary>
        /// 显示列表选中事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ListBoxH_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            foreach (var item in e.RemovedItems)
            {
                var temp = item.GetType().GetProperty(SelectItemKey).GetValue(item, null).ToString();
                for (int i = 0; i < _ListBoxV.SelectedItems.Count; i++)
                {
                    if (temp == _ListBoxV.SelectedItems[i].GetType().GetProperty(SelectItemKey).GetValue(_ListBoxV.SelectedItems[i], null).ToString())
                    {
                        _ListBoxV.SelectedItems.Remove(_ListBoxV.SelectedItems[i]);
                    }
                }
            }
        }

        /// <summary>
        /// 下拉列表选中事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void ListBoxV_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            foreach (var item in e.AddedItems)
            {
                var key = item.GetType().GetProperty(SelectItemKey).GetValue(item, null).ToString();
                SelectlistItem selectlistItem = null;
                bool flag = true;
                foreach (var subItem in SelectList)
                {
                    var subKey = subItem.GetType().GetProperty(SelectItemKey).GetValue(subItem, null).ToString();
                    if (key == subKey)
                    {
                        var temp = subItem;
                        temp.IsSelected = true;
                        selectlistItem = temp;
                        break;
                    }
                }

                foreach (var subItem in ChekedItems)
                {
                    var subKey = subItem.GetType().GetProperty(SelectItemKey).GetValue(subItem, null).ToString();
                    if (key == subKey)
                    {
                        flag = false;
                        break;
                    }
                }
                if (flag)
                {
                    ChekedItems.Add(selectlistItem);
                }
            }

            foreach (var item in e.RemovedItems)
            {
                var key = item.GetType().GetProperty(SelectItemKey).GetValue(item, null).ToString();
                if (ChekedItems == null) return;
                SelectlistItem temp = null;
                foreach (var subItem in ChekedItems)
                {
                    if (subItem.Id == key)
                    {
                        temp = subItem;
                        break;
                    }
                }
                if (temp != null)
                {
                    ChekedItems.Remove(temp);
                }
            }

            if (ChekedItems != null && ChekedItems.Count > 0)
            {
                string temp = "";
                foreach (var item in ChekedItems)
                {
                    temp = temp + item.Id;
                }
                Text = temp;
            }
            else
            {
                Text = string.Empty;
            }

            OnEndSelected();
        }


        #region event
        public event EventHandler EndSelected;

        public static readonly DependencyProperty EndSelectedCommandProperty = DependencyProperty.Register(
            "EndSelectedCommand",
            typeof(ICommand),
            typeof(MultiSelect)
            );

        public ICommand EndSelectedCommand
        {
            get { return (ICommand)GetValue(EndSelectedCommandProperty); }
            set { SetValue(EndSelectedCommandProperty, value); }
        }

        /// <summary>
        /// fire event and command
        /// </summary>
        protected void OnEndSelected()
        {
            EndSelected?.Invoke(this, EventArgs.Empty);

            if (EndSelectedCommand != null && EndSelectedCommand.CanExecute(ChekedItems))
            {
                EndSelectedCommand.Execute(ChekedItems);
            }
        }
        #endregion

        /// <summary>
        /// 获得LisBox的ScrollViewer
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static T FindVisualChild<T>(DependencyObject obj) where T : DependencyObject
        {
            if (obj != null)
            {
                for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
                {
                    DependencyObject child = VisualTreeHelper.GetChild(obj, i);
                    if (child != null && child is T)
                    {
                        return (T)child;
                    }
                    T childItem = FindVisualChild<T>(child);
                    if (childItem != null) return childItem;
                }
            }
            return null;
        }

        /// <summary>
        /// ListBox横向滚动滚轮事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void List_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            ItemsControl items = (ItemsControl)sender;
            ScrollViewer scroll = FindVisualChild<ScrollViewer>(items);
            if (scroll != null)
            {
                int d = e.Delta;
                if (d > 0)
                {
                    scroll.LineRight();
                }
                if (d < 0)
                {
                    scroll.LineLeft();
                }
                scroll.ScrollToTop();
            }
        }
    }
}
